package com.tfswx.jcyw.cloudfiletest;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.*;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Ricky
 * @date 2021/1/27
 */
@Slf4j
public class HttpUtil {
    private static final String SEND_LOG_PATTERN = "==>  {}  {}";

    private static RestTemplate restTemplate;

    private static int port;

    static {
        // 长连接保持30秒
        PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        // 总连接数
        pollingConnectionManager.setMaxTotal(1000);
        // 同路由的并发数
        pollingConnectionManager.setDefaultMaxPerRoute(1000);

        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        // 重试次数，默认是3次，没有开启
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true));
        // 保持长连接配置，需要在头添加Keep-Alive
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());

        //        RequestConfig.Builder builder = RequestConfig.custom();
        //        builder.setConnectionRequestTimeout(200);
        //        builder.setConnectTimeout(5000);
        //        builder.setSocketTimeout(5000);
        //
        //        RequestConfig requestConfig = builder.build();
        //        httpClientBuilder.setDefaultRequestConfig(requestConfig);

        //        List<Header> headers = new ArrayList<>();
        //        headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
        //        headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
        //        headers.add(new BasicHeader("Accept-Language", "zh-CN"));
        //        headers.add(new BasicHeader("Connection", "Keep-Alive"));
        //
        //        httpClientBuilder.setDefaultHeaders(headers);

        HttpClient httpClient = httpClientBuilder.build();


        // httpClient连接配置，底层是配置RequestConfig
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        // 连接超时
        clientHttpRequestFactory.setConnectTimeout(50000);
        // 数据读取超时时间，即SocketTimeout
        clientHttpRequestFactory.setReadTimeout(50000);
        // 连接不够用的等待时间，不宜过长，必须设置，比如连接不够用时，时间过长将是灾难性的
        clientHttpRequestFactory.setConnectionRequestTimeout(200);
        // 缓冲请求数据，默认值是true。通过POST或者PUT大量发送数据时，建议将此属性更改为false，以免耗尽内存。
        // clientHttpRequestFactory.setBufferRequestBody(false);

        restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(clientHttpRequestFactory);
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        supportedMediaTypes.add(MediaType.APPLICATION_PDF);
        supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XML);
        supportedMediaTypes.add(MediaType.IMAGE_GIF);
        supportedMediaTypes.add(MediaType.IMAGE_JPEG);
        supportedMediaTypes.add(MediaType.IMAGE_PNG);
        supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
        supportedMediaTypes.add(MediaType.TEXT_HTML);
        supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
        supportedMediaTypes.add(MediaType.TEXT_PLAIN);
        supportedMediaTypes.add(MediaType.TEXT_XML);
        fastJsonHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
        messageConverters.add(fastJsonHttpMessageConverter);
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            if ("StringHttpMessageConverter".equals(messageConverter.getClass().getSimpleName())) {
                StringHttpMessageConverter messageConverter1 = (StringHttpMessageConverter) messageConverter;
                messageConverter1.setDefaultCharset(StandardCharsets.UTF_8);
            }
        }
        //        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
    }

    private HttpUtil() {
    }

    public static RestTemplate getRestTemplate() {
        return restTemplate;
    }

    public static <T> T postForObject(String url, Class<T> responseType, Object... uriVariables) {
        return postForObject(url, null, responseType, uriVariables);
    }

    public static <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) {
        long startTime = System.currentTimeMillis();
        try {
            //            log.info("开始调用接口：{},参数：{}", url, JSON.toJSONString(request));
            return getRestTemplate().postForObject(url, request, responseType, uriVariables);
        } finally {
            log.info(SEND_LOG_PATTERN, url, System.currentTimeMillis() - startTime);
        }
    }

    public static <T> T getForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) {
        Map<String, Object> map = JSON.parseObject(JSON.toJSONString(request));
        StringBuilder param = new StringBuilder();
        String s = "?";
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            param.append(s).append(entry.getKey()).append("=").append(entry.getValue());
            s = "&";
        }
        long startTime = System.currentTimeMillis();
        try {
            return getRestTemplate().getForObject(url + param, responseType, uriVariables);
        } finally {
            log.info(SEND_LOG_PATTERN, url, System.currentTimeMillis() - startTime);
        }
    }

    public static <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) {
        long startTime = System.currentTimeMillis();
        try {
            return getRestTemplate().getForObject(url, responseType, uriVariables);
        } finally {
            log.info(SEND_LOG_PATTERN, url, System.currentTimeMillis() - startTime);
        }
    }

    public static File downloadFile(String url, String filePath) {
        return downloadFile(url, filePath);
    }

    public static File downloadFile(String url, File file) {
        return getRestTemplate().execute(url, HttpMethod.GET, null, response -> {
            HttpHeaders headers = response.getHeaders();
            long contentLength = headers.getContentLength();
            File canonicalFile = file.getCanonicalFile();
            if (!canonicalFile.getParentFile().exists()) {
                boolean b = canonicalFile.getParentFile().mkdirs();
                if (!b) {
                    log.debug("创建失败");
                }
            }
            try (FileOutputStream fileOutputStream = new FileOutputStream(canonicalFile); InputStream inputStream = response.getBody()) {
                byte[] buffer = new byte[1024 * 1024];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, len);
                }
            }
            return canonicalFile;
        });
    }


    public static <T> T apiGetForObject(String jkbm, Object request, Class<T> responseType, Object... uriVariables) {
        try {
            JSONObject forObject = HttpUtil.getForObject("http://127.0.0.1:" + port + "/api/" + jkbm, request, JSONObject.class, uriVariables);
            return getObject(forObject, responseType);
        } catch (HttpServerErrorException e) {
            String responseBodyAsString = e.getResponseBodyAsString();
            JSONObject jsonObject = JSON.parseObject(responseBodyAsString);
            return getObject(jsonObject, responseType);
        }
    }

    public static <T> T apiPostForObject(String jkbm, Object request, Class<T> responseType, Object... uriVariables) {
        try {
            JSONObject forObject = HttpUtil.postForObject("http://127.0.0.1:" + port + "/api/" + jkbm, request, JSONObject.class, uriVariables);
            return getObject(forObject, responseType);
        } catch (HttpServerErrorException e) {
            String responseBodyAsString = e.getResponseBodyAsString();
            JSONObject jsonObject = JSON.parseObject(responseBodyAsString);
            return getObject(jsonObject, responseType);
        }
    }

    public static <T> List<T> apiGetForList(String jkbm, Object request, Class<T> responseType, Object... uriVariables) {
        try {
            JSONObject forObject = HttpUtil.getForObject("http://127.0.0.1:" + port + "/api/" + jkbm, request, JSONObject.class, uriVariables);
            return getList(forObject, responseType);
        } catch (HttpServerErrorException e) {
            String responseBodyAsString = e.getResponseBodyAsString();
            JSONObject jsonObject = JSON.parseObject(responseBodyAsString);
            return getList(jsonObject, responseType);
        }
    }

    public static <T> List<T> apiPostForList(String jkbm, Object request, Class<T> responseType, Object... uriVariables) {
        try {
            JSONObject forObject = HttpUtil.postForObject("http://127.0.0.1:" + port + "/api/" + jkbm, request, JSONObject.class, uriVariables);
            return getList(forObject, responseType);
        } catch (HttpServerErrorException e) {
            String responseBodyAsString = e.getResponseBodyAsString();
            JSONObject jsonObject = JSON.parseObject(responseBodyAsString);
            return getList(jsonObject, responseType);
        }
    }

    private static <T> T getObject(JSONObject forObject, Class<T> responseType) {
        int code = forObject.getIntValue("code");
        if (code != 0) {
            throw new RuntimeException(forObject.getString("message"));
        }
        return forObject.getObject("data", responseType);
    }

    private static <T> List<T> getList(JSONObject forObject, Class<T> responseType) {
        int code = forObject.getIntValue("code");
        if (code != 0) {
            throw new RuntimeException(forObject.getString("message"));
        }
        JSONArray data = forObject.getJSONArray("data");
        if (data == null) {
            return new ArrayList<>();
        }
        return data.toJavaList(responseType);
    }
}
